package com.freight.file.serviceimpl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.freight.core.common.constant.CommonConstant;
import com.freight.core.common.constant.SettingConstant;
import com.freight.core.common.exception.FreightException;
import com.freight.core.common.redis.RedisTemplateHelper;
import com.freight.core.common.utils.*;
import com.freight.core.common.vo.Result;
import com.freight.core.common.vo.SearchVo;
import com.freight.core.entity.Setting;
import com.freight.core.entity.User;
import com.freight.core.service.SettingService;
import com.freight.core.vo.OssSetting;
import com.freight.file.dao.FileDao;
import com.freight.file.entity.File;
import com.freight.file.manage.FileManageFactory;
import com.freight.file.service.FileService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.criteria.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 文件管理接口实现
 *
 * @describe
 */
@Slf4j
@Service
@Transactional
@CacheConfig(cacheNames = "file")
public class FileServiceImpl implements FileService {

    @Autowired
    private FileDao fileDao;

    @Autowired
    private SettingService settingService;

    @Autowired
    private FileManageFactory fileManageFactory;

    @Value("${file-save-path}")
    private String fileSavePath;

    @Autowired
    SecurityUtil securityUtil;


    @Autowired
    private RedisTemplateHelper redisTemplate;

    @Override
    public FileDao getRepository() {
        return fileDao;
    }

    @Override
    @Cacheable(key = "#id")
    public File get(Long id) {

        // 避免缓存穿透
        String result = redisTemplate.get("file::" + id);
        if ("null".equals(result)) {
            return null;
        }
        File file = fileDao.findById(id).orElse(null);
        if (file == null) {
            redisTemplate.set("file::" + id, "null", 5L, TimeUnit.MINUTES);
        }
        return file;
    }

    /**
     * 判断文件类型
     * @param suffix
     * @Author Smily
     * @DateTime 2021年2月7日
     * @return
     */
    public static boolean isImage(String suffix) {
        suffix = suffix.toUpperCase();
        if (".PNG".equals(suffix)
                || ".JPG".equals(suffix)
                || ".JEPG".equals(suffix)
                || ".JPEG".equals(suffix)
                || ".BMP".equals(suffix)
                || ".HEIC".equals(suffix)) {
            return true;
        }
        return false;
    }

    /**
     * 获取文件后缀
     * @Author Smily
     * @DateTime 2021年2月7日
     * @param filename
     * @return
     */
    private String getSuffix(String filename) {
        if (StringUtils.isNotBlank(filename)){
            String suffix = "";
            int pos = filename.lastIndexOf('.');
            if (pos > 0 && pos < filename.length() - 1) {
                suffix = filename.substring(pos);
            }
            return suffix.toLowerCase();
        }
        return "";
    }

    @Override
    public Page<File> findByCondition(File file, SearchVo searchVo, Pageable pageable) {

        return fileDao.findAll(new Specification<File>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<File> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

                Path<String> nameField = root.get("name");
                Path<String> fKeyField = root.get("fKey");
                Path<String> typeField = root.get("type");
                Path<String> createByField = root.get("createBy");
                Path<Integer> locationField = root.get("location");
                Path<Date> createTimeField = root.get("createTime");

                List<Predicate> list = new ArrayList<>();

                // 模糊搜素
                if (StrUtil.isNotBlank(file.getName())) {
                    list.add(cb.like(nameField, '%' + file.getName() + '%'));
                }
                if (StrUtil.isNotBlank(file.getFKey())) {
                    list.add(cb.like(fKeyField, '%' + file.getFKey() + '%'));
                }
                if (StrUtil.isNotBlank(file.getType())) {
                    list.add(cb.like(typeField, '%' + file.getType() + '%'));
                }
                if (StrUtil.isNotBlank(file.getCreateBy())) {
                    list.add(cb.like(createByField, '%' + file.getCreateBy() + '%'));
                }

                if (file.getLocation() != null) {
                    list.add(cb.equal(locationField, file.getLocation()));
                }

                // 创建时间
                if (StrUtil.isNotBlank(searchVo.getStartDate()) && StrUtil.isNotBlank(searchVo.getEndDate())) {
                    Date start = DateUtil.parse(searchVo.getStartDate());
                    Date end = DateUtil.parse(searchVo.getEndDate());
                    list.add(cb.between(createTimeField, start, DateUtil.endOfDay(end)));
                }

                Predicate[] arr = new Predicate[list.size()];
                cq.where(list.toArray(arr));
                return null;
            }
        }, pageable);
    }


    public static void main(String[] args) {
        String b="";
        new FileServiceImpl().saveFile(null,b,"");
    }

    @Override
    public Result<Object> saveFile(MultipartFile file, String base64, String fileType) {
        User u = securityUtil.getCurrUser();

        Setting setting = settingService.get(SettingConstant.OSS_USED);
        if (setting == null || StrUtil.isBlank(setting.getValue())) {
            return ResultUtil.error(501, "您还未配置OSS存储服务");
        }
        if (StrUtil.isNotBlank(base64)) {
            // base64上传
            file = Base64DecodeMultipartFile.base64Convert(base64);
        }
        String result = "";
        String fKey = CommonUtil.renamePic(file.getOriginalFilename());
        File f = new File();
        // f = fileDao.findAllByUserIdAndFileType(u.getId(), fileType);
        if (f == null) {
            f = new File();
        }
        f.setUserId(u == null ? 0 : u.getId());
        try {
            InputStream inputStream = file.getInputStream();
            // 上传至第三方云服务或服务器
            result = fileManageFactory.getFileManage(null).inputStreamUpload(inputStream, fKey, file);
            // 保存数据信息至数据库

            f.setLocation(getType(setting.getValue())).setName(file.getOriginalFilename()).setSize(file.getSize())
                    .setType(file.getContentType()).setFKey(fKey).setUrl(result);

            f.setNickName(u.getNickName());
            f.setFileType(fileType);
            fileDao.save(f);
        } catch (Exception e) {
            log.error(e.toString());
            return ResultUtil.error(e.toString());
        }
        if (setting.getValue().equals(SettingConstant.LOCAL_OSS)) {
            OssSetting os = new Gson().fromJson(settingService.get(SettingConstant.LOCAL_OSS).getValue(), OssSetting.class);
            result = os.getHttp() + os.getEndpoint() + "/" + f.getId();
        }
        return ResultUtil.data(result);
    }

    @Override
    public File saveFileByUserId(MultipartFile file,String fileType) {
        User u = securityUtil.getCurrUser();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        File res = null;
        Setting setting = settingService.get(SettingConstant.OSS_USED);
        String fKey = CommonUtil.renamePic(file.getOriginalFilename());
        File f = new File();
        f.setUserId(u.getId());
        try {
            String fileName = file.getOriginalFilename();
            //根据文件类型储存在不同的文件夹下
            String extension = getSuffix(fileName);//文件格式
            if ((".pjpeg").equals(extension)) {//图片扩展名=转换
                extension = ".jpeg";
            }

            //文件存储目录
            String datePath = new SimpleDateFormat("yyyy/MM/dd/").format(new Date());//根据日期创建不同目录
            String folderPath = "image/" + datePath;
            //文件名 普通地址 命名方式:时间-数字-描述
            String newFileName = sdf.format(new Date()) + extension;

            java.io.File targetFile = new java.io.File(fileSavePath + folderPath, newFileName);
            if (!targetFile.exists()) targetFile.mkdirs();
            file.transferTo(targetFile);

            // 保存数据信息至数据库
            f.setLocation(getType(setting.getValue())).setName(file.getOriginalFilename()).setSize(file.getSize())
                    .setType(file.getContentType()).setFKey(fKey).setUrl(folderPath+newFileName);
            f.setNickName(u.getNickName());
            f.setFileType(fileType);
            log.info(f.toString());
            res = fileDao.save(f);
        } catch (Exception e) {
            log.error(e.toString());
            return res;
        }

        return res;
    }

    public Integer getType(String type) {
        switch (type) {
            case SettingConstant.QINIU_OSS:
                return CommonConstant.OSS_QINIU;
            case SettingConstant.ALI_OSS:
                return CommonConstant.OSS_ALI;
            case SettingConstant.TENCENT_OSS:
                return CommonConstant.OSS_TENCENT;
            case SettingConstant.MINIO_OSS:
                return CommonConstant.OSS_MINIO;
            case SettingConstant.LOCAL_OSS:
                return CommonConstant.OSS_LOCAL;
            default:
                return -1;
        }

    }
}